
#include "samplers.fx"
#include "shading.fx"

struct VS_IN
{
	float3 position : POSITION;
	float3 normal	: NORMAL;
	float3 tangent : TANGENT;
	float2 tex		: TEX_COORDS;
};

struct VS_OUT
{
	float3	position	:	POSITION;
	float3	normal		:	NORMAL;
	float2  tex			:	TEX_COORDS;
};

struct GS_OUT
{
	float4 position		:	SV_POSITION;
	float3 normal		:	NORMAL;
	float2 tex			:	TEXCOORDS;
};

struct Material
{
	float4 ambient;
	float4 diffuse;
	float4 specular;
	float4 vIsTextured;	// x:diffuse, y:specular, z:normal
};
Texture2D	tex_diffuse;
Texture2D	tex_specular;
Texture2D	tex_normal;
Texture2D   tex_noise;

Texture2D	texTerrainHeight;

float4x4 m_World;
float4x4 m_WorldViewProj;
float4x4 m_View;
float4x4 m_Proj;

float	fThornLength;


float getTerrainHeight(float3 wpos)
{
	float GRID_WIDTH = 200.0f;

	float u = wpos.x / GRID_WIDTH + 0.5f;
	float v = wpos.y / GRID_WIDTH + 0.5f;

	return texTerrainHeight.SampleLevel(s_linear, float2(u,v), 0).x;

}


float3 alienVertexTransform(float3 position)
{	
	float3 modified_Lpos = position;

	// ____VS-based alien transform

	// __a: arms! alien will have very long arms
	if (position.x > 1.8f)		 // R
		modified_Lpos.x += 1.5f;
	else if (position.x < -1.8f) // L
		modified_Lpos.x -= 1.5f;
	
	// __b: legs! merge the two legs
	else if (position.y < -0.4f) {
		// amount of translation depends on the y value
		float t = smoothstep(-3.9f, 0.4f, position.y);
		t = (1.0f - t) * 0.40f;
		modified_Lpos.x  -= sign(position.x) * t;

		// legs lower part (shoes)
		if (position.y < -3.2f)
			modified_Lpos *= float3(0.5f, 1.0f, 0.3f);
	}

	// __c: tail! translate the vertices at the back of the model
	else if (position.z < 0.0f && abs(position.x) < 0.2f &&
								  abs(position.y+0.3f) < 0.2f)
		modified_Lpos += float3(0, 1.5f, -1.5f);

	return modified_Lpos;
}

VS_OUT VS_Alien(VS_IN vin)
{
	VS_OUT vout;

	float3 modifiedPos = alienVertexTransform(vin.position);

	vout.position = modifiedPos;
	vout.normal = vin.normal;
	vout.tex = vin.tex;

	return vout;
}

float3 getRandomVector(float2 uv)
{
	float3 ret = tex_noise.SampleLevel(s_linear, uv, 0).xyz;
	return ret;
}

[maxvertexcount(12)]
void GS_Alien(triangle VS_OUT input[3], inout TriangleStream<GS_OUT> gstream, uint PrimitiveId : SV_PRIMITIVEID )
{
	// compute the triangle normal
	float3 e1 = input[1].position - input[0].position;
	float3 e2 = input[2].position - input[0].position;
	float3 triNormal = normalize(cross(e1, e2));

	// compute the center of the triangle
	float3 cntPos = (	input[0].position + 
	                    input[1].position +
						input[2].position
					) / 3.0f;


	float3 thorn_dir = 0.3f * triNormal +
	                   0.7f * normalize(float3(0.0f, 1.0f, -0.3f));


	float minLength = 0.5f;
	float maxLength = 2.5f;
	float l = minLength + (maxLength-minLength) * fThornLength; 

	// sample a noise texture to create thorns of different lengths
	float3 rnd = getRandomVector(float2(PrimitiveId*input[0].tex.x, PrimitiveId*input[1].tex.y));
	cntPos +=  thorn_dir * (rnd.x+rnd.y+rnd.z) * l; // 0.5, 2.5

	// tex coords for the center point
	float2 cntTex = (	input[0].tex + 
						input[1].tex + 
						input[2].tex
					) / 3.0f;

	GS_OUT gout;
	
	float3 wpos = float3(m_World._41, m_World._42, m_World._43);
	float3 pos;

	// THORNS
	if (input[0].position.y > 2.4f && 
		input[1].position.y > 2.4f &&
		input[2].position.y > 2.4f)
	{
		for (uint i=0; i<3; i++)
		{
			// new normal
			float3 e1 = cntPos - input[i].position;
			uint index = (i+1) % 3;
			float3 e2 = cntPos - input[index].position;
			float3 N = normalize(cross(e1, e2));

			pos = input[i].position;
			pos.y  += getTerrainHeight(wpos) * 10.0f - 15.0f;
			gout.position = mul(float4(pos, 1.0f), m_WorldViewProj);
			gout.normal =   input[i].normal + 0.2f * N;
			gout.tex =      input[i].tex;
			gstream.Append(gout);

			pos = input[index].position;
			pos.y  += getTerrainHeight(wpos) * 10.0f - 15.0f;
			gout.position = mul(float4(pos, 1.0f), m_WorldViewProj);
			gout.normal =	input[index].normal + 0.2f * N;
			gout.tex =		input[index].tex;
			gstream.Append(gout);

			pos = cntPos;
			pos.y  += getTerrainHeight(wpos) * 10.0f - 15.0f;
			gout.position = mul(float4(pos, 1.0f), m_WorldViewProj);
			gout.normal =	triNormal;
			gout.tex =		cntTex;
			gstream.Append(gout);
		}
	}
	// NO thorns
	else {
		for (int i=0; i<3; i++)
		{
			pos = input[i].position;
			pos.y  += getTerrainHeight(wpos) * 10.0f - 15.0f;
			gout.position = mul(float4(pos, 1.0f), m_WorldViewProj);
			gout.normal = input[i].normal;
			gout.tex = input[i].tex;
			gstream.Append(gout);
		}
	}
}


float4 PS_Alien(GS_OUT pin) : SV_TARGET
{
	float3 diffuse = tex_diffuse.Sample(s_linear, pin.tex).xyz;

	diffuse *= shade(pin.normal);

	return float4(diffuse,1);
}


RasterizerState rs_Wireframe
{
	CullMode = Back;
	FillMode = Wireframe;
};


technique10 AlienGS
{
	pass P0
	{
		SetVertexShader( CompileShader( vs_4_0,  VS_Alien()   ) );
		SetHullShader ( NULL );
		SetDomainShader( NULL );
		SetGeometryShader( CompileShader(gs_4_0, GS_Alien()   ) );
		SetPixelShader( CompileShader( ps_4_0,   PS_Alien()   ) );	
	}
}
